/*
 * Utility.java   ver    30/ott/2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */
package org.mentalsmash.tazio.transactions.util;

import java.io.Serializable;
import java.util.ArrayList;

import org.mentalsmash.tazio.IllegalArgumentException;
import org.mentalsmash.tazio.transactions.Transactional;

import com.google.inject.internal.Lists;

/**
 * @author Andrea Reale
 * @version 0.0.1
 */
public class Utility {
    public static boolean isPrimitiveType(final Serializable object) {
	final Class<?> type;
	if (object == null) {
	    return false;
	}
	type = object.getClass();

	return isPrimitive(type);

    }

    public static boolean isPrimitive(final Class<?> type) {
	final Class<?> primitiveClasses[] = { Boolean.class, Byte.class,
		Character.class, Short.class, Integer.class, Long.class,
		Float.class, Double.class, String.class };

	if (type == null) {
	    return false;
	}

	if (type.isPrimitive()) {
	    return true;
	} else {
	    for (final Class<?> currPrimitive : primitiveClasses) {
		if (type.equals(currPrimitive)) {
		    return true;
		}
	    }
	    return false;
	}
    }

    /**
     * Utility method that - given a Serializable object instance - returns the
     * list of interfaces for that object implementing the {@link Transactional}
     * annotation
     * 
     * @param obj
     *            the instance to found annotated interfaces on
     * @return an array of interfaces annoteted with the {@link Transactional}
     *         annotation, or <code>null</code> if non was found
     */
    public static Class<?>[] findTransactionalInterfaces(final Serializable obj) {
	final Class<?> type;

	if (obj == null) {
	    return null;
	}

	type = obj.getClass();

	return findTransactionalInterfaces(type);
    }

    /**
     * Utility method that - given a type instance - returns the list of
     * interfaces for that object implementing the {@link Transactional}
     * annotation
     * 
     * @param obj
     *            the instance to found annotated interfaces on
     * @return an array of interfaces annoteted with the {@link Transactional}
     *         annotation, or <code>null</code> if non was found
     */
    public static Class<?>[] findTransactionalInterfaces(final Class<?> type) {
	final ArrayList<Class<?>> interfaces = Lists.newArrayList();
	final ArrayList<Class<?>> toRet;

	if (type == null) {
	    return null;
	}

	if (type.isInterface()) {
	    interfaces.add(type);
	}
	
	for (final Class<?> ifc : type.getInterfaces()) {
	    interfaces.add(ifc);
	}
	toRet = Lists.newArrayList();

	for (final Class<?> iface : interfaces) {
	    if (iface.isAnnotationPresent(Transactional.class)) {
		toRet.add(iface);
	    }
	}
	if (toRet.isEmpty()) {
	    return null;
	} else {
	    return toRet.toArray(new Class<?>[toRet.size()]);
	}
    }

    /**
     * Utility method that - given a type - checks whether that type implements
     * a given interface.
     * <p>
     * If the <code>type</code> argument is a <code>Class</code> the method
     * returns true if the class (or one of its superclasses) implements the
     * interface <code>type</code>
     * </p>
     * <p>
     * If the <code>type</code> argument is an <code>Interface</code> the method
     * returns <code>true</code> if the interface is of the given type, or the
     * interface of the given type is one of its super-interfaces.
     * </p>
     * 
     * @param type
     *            the type to check
     * @param iface
     *            the interface that type should implement
     * @return true if <code>type</code> implements <code>iface</code>
     */
    public static boolean implementsInterface(final Class<?> type,
	    final Class<?> iface) {

	final ArrayList<Class<?>> interfaces;
	if (!iface.isInterface()) {
	    throw new IllegalArgumentException("Not an interface");
	}
	interfaces = new ArrayList<Class<?>>();

	if (type == null) {
	    return false;
	}

	if (type.isInterface()) {
	    interfaces.add(type);
	}
	for (final Class<?> ifc : type.getInterfaces()) {
	    interfaces.add(ifc);
	}

	return interfaces.contains(iface);
    }

    public static boolean implementsInterface(final Object obj,
	    final Class<?> iface) {
	final Class<?> type = obj.getClass();
	return implementsInterface(type, iface);
    }

}
